package org.liveboardgames.agricola.domain.card;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.liveboardgames.agricola.domain.action.Action;
import org.liveboardgames.agricola.domain.common.IResourceContainer;
import org.liveboardgames.agricola.domain.common.Peep;
import org.liveboardgames.agricola.domain.common.SpecialPoint;
import org.liveboardgames.agricola.domain.common.Target;
import org.liveboardgames.agricola.model.enums.ChangingTypeEnum;
import org.liveboardgames.agricola.model.enums.GameTypeEnum;
import org.liveboardgames.agricola.model.enums.ResourceZoneEnum;
import org.liveboardgames.agricola.model.structure.Visitable;
import org.liveboardgames.agricola.model.structure.Visitor;

public class Card implements Target, Comparable<Card>, Visitable, IResourceContainer {

  private String cardName;

  private Long cardId;

  private ReferenceCard refCard;

  private Set<Action> actions;

  private Set<Action> exclusiveActions;

  private String imagePath;

  private Integer points;

  private Set<SpecialPoint> specialPoints;

  private ResourceZoneEnum resourceZone;

  private Integer numberOfPlayers;

  private GameTypeEnum familyType;

  private ChangingTypeEnum changingType;

  private Integer period;

  private Map<String, Integer> resources;

  private Set<Peep> peeps;

  /**
   * @return the {@link #cardName}
   */
  public String getCardName() {
    return cardName;
  }

  /**
   * @param cardName
   *          the {@link #cardName} to set
   */
  public void setCardName(String cardName) {
    this.cardName = cardName;
  }

  /**
   * @return the {@link #cardId}
   */
  public Long getCardId() {
    return cardId;
  }

  /**
   * @param cardId
   *          the {@link #cardId} to set
   */
  public void setCardId(Long cardId) {
    this.cardId = cardId;
  }

  /**
   * @return the {@link #actions}
   */
  public Set<Action> getActions() {
    return actions;
  }

  /**
   * @param actions
   *          the {@link #actions} to set
   */
  public void setActions(Set<Action> actions) {
    this.actions = actions;
  }

  /**
   * @return the {@link #exclusiveActions}
   */
  public Set<Action> getExclusiveActions() {
    return exclusiveActions;
  }

  /**
   * @param exclusiveActions
   *          the {@link #exclusiveActions} to set
   */
  public void setExclusiveActions(Set<Action> exclusiveActions) {
    this.exclusiveActions = exclusiveActions;
  }

  /**
   * @return the {@link #resources}
   */
  public Map<String, Integer> getResources() {
    return resources;
  }

  /**
   * @param resources
   *          the {@link #resources} to set
   */
  public void setResources(Map<String, Integer> resources) {
    this.resources = resources;
  }

  /**
   * @return the {@link #peeps}
   */
  public Set<Peep> getPeeps() {
    return peeps;
  }

  /**
   * @param peeps
   *          the {@link #peeps} to set
   */
  public void setPeeps(Set<Peep> peeps) {
    this.peeps = peeps;
  }

  public void addPeep(Peep peep) {
    if (this.peeps == null) {
      this.peeps = new HashSet<Peep>();
    }
    this.peeps.add(peep);
  }

  /**
   * @return the {@link #imagePath}
   */
  public String getImagePath() {
    return imagePath;
  }

  /**
   * @param imagePath
   *          the {@link #imagePath} to set
   */
  public void setImagePath(String imagePath) {
    this.imagePath = imagePath;
  }

  /**
   * @return the {@link #points}
   */
  public Integer getPoints() {
    return points;
  }

  /**
   * @param points
   *          the {@link #points} to set
   */
  public void setPoints(Integer points) {
    this.points = points;
  }

  /**
   * @return the {@link #specialPoints}
   */
  public Set<SpecialPoint> getSpecialPoints() {
    return specialPoints;
  }

  /**
   * @param specialPoints
   *          the {@link #specialPoints} to set
   */
  public void setSpecialPoints(Set<SpecialPoint> specialPoints) {
    this.specialPoints = specialPoints;
  }

  /**
   * @return the {@link #resourceZone}
   */
  public ResourceZoneEnum getResourceZone() {
    return resourceZone;
  }

  /**
   * @param resourceZone
   *          the {@link #resourceZone} to set
   */
  public void setResourceZone(ResourceZoneEnum resourceZone) {
    this.resourceZone = resourceZone;
  }

  public void setNumberOfPlayers(Integer numberOfPlayers) {
    this.numberOfPlayers = numberOfPlayers;
  }

  public Integer getNumberOfPlayers() {
    return numberOfPlayers;
  }

  public void setFamilyType(GameTypeEnum familyType) {
    this.familyType = familyType;
  }

  public GameTypeEnum getFamilyType() {
    return familyType;
  }

  /**
   * @param changingType
   *          the changingType to set
   */
  public void setChangingType(ChangingTypeEnum changingType) {
    this.changingType = changingType;
  }

  /**
   * @return the changingType
   */
  public ChangingTypeEnum getChangingType() {
    return changingType;
  }

  public void setPeriod(Integer period) {
    this.period = period;
  }

  public Integer getPeriod() {
    return period;
  }

  public int compareTo(Card o) {
    int result = 0;
    if (o != null && this.refCard != null && o.getRefCard() != null) {
      result = this.getRefCard().getCardId() - o.getRefCard().getCardId();
    }
    return result;
  }

  public void setRefCard(ReferenceCard refCard) {
    this.refCard = refCard;
  }

  public ReferenceCard getRefCard() {
    return refCard;
  }

  public void addResource(String type, Integer quantity) {
    if (this.resources == null) {
      this.resources = new HashMap<String, Integer>();
    }
    Integer currentQuantity = this.resources.get(type);
    this.resources.put(type, Math.max(0, currentQuantity + quantity));
  }

  public void addResources(Map<String, Integer> resources) {
    if (resources != null) {
      for (String type : resources.keySet()) {
        addResource(type, resources.get(type));
      }
    }
  }

  public void acceptVisit(Visitor visitor) {
    if (this.actions != null) {
      for (Action action : this.actions) {
        action.acceptVisit(visitor);
      }
    }

    if (this.exclusiveActions != null) {
      for (Action action : this.exclusiveActions) {
        action.acceptVisit(visitor);
      }
    }

    visitor.visit(this);
  }

  @Override
  public boolean equals(Object obj) {
    return this.cardId == null ? false : (obj instanceof Card && ((Card)obj).cardId.equals(this.cardId));
  }

  @Override
  public int hashCode() {
    return this.cardId == null ? 0 : this.cardId.intValue();
  }
}
